## 闭包

闭包之所以被称为“闭包”，是因为它能够“闭合”其作用域。闭包是指有权访问另一个函数作用域内变量的函数。

```html
维基百科定义: 在计算机科学中，闭包（英语：Closure），又称词法闭包（Lexical
Closure）或函数闭包（function closures），
是在支持头等函数的编程语言中实现词法绑定的一种技术。
```

```html
MDN定义: 闭包（closure）是一个函数以及其捆绑的周边环境状态（lexical
environment，词法环境）的引用的组合。
换而言之，闭包让开发者可以从内部函数访问外部函数的作用域。在 JavaScript
中，闭包会随着函数的创建而被同时创建。
```

```
词法（lexical）一词指的是，词法作用域根据源代码中声明变量的位置来确定该变量在何处可用。
嵌套函数可访问声明于它们外部作用域的变量。
```

作用域应用的特殊情况，有两种表现：
函数作为参数被传递
函数作为返回值被返回

```js
function makeFunc() {
  var name = "Mozilla";
  function displayName() {
    alert(name);
  }
  return displayName;
}

var myFunc = makeFunc();
myFunc();
```

JavaScript 中的函数会形成了闭包。 闭包是由函数以及声明该函数的词法环境组合而成的。该环境包含了这个闭包创建时作用域内的任何局部变量。在本例子中，myFunc 是执行 makeFunc 时创建的 displayName 函数实例的引用。displayName 的实例维持了一个对它的词法环境（变量 name 存在于其中）的引用。因此，当 myFunc 被调用时，变量 name 仍然可用，其值 Mozilla 就被传递到 alert 中。

```js
// 函数作为返回值
function create() {
  let a = 100;
  return function () {
    console.log(a);
  };
}
```

```js
// 函数作为参数
function create(fn) {
  let a = 100;
  fn();
}

let a = 100;

function fn() {
  console.log(a);
}

create(fn);
```

闭包的应用

- 隐藏数据
- 如做一个简单的 cache 工具

```js
// 闭包隐藏数据，只提供 API
function createCache() {
  // 据，被隐藏，不被外界访问
  return {
    set: function (key, val) {
      data[key] = val;
    },
    get: function (key) {
      return data[key];
    },
  };
}

const c = createCache();
c.set("a", 100);
console.log(c.get("a"));
```

### 闭包技巧一：模拟私有方法

https://www.gracecode.com/posts/2385.html  
http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html  
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures  
https://juejin.cn/post/7054205481190948894

影响：影响：变量会常驻内存，得不到释放。闭包不要乱用

闭包是将函数内部和函数外部连接起来的桥梁

在 JavaScript 中，闭包指的是函数和函数体内定义的变量的组合。闭包允许函数访问在它外部定义的变量，即使这些变量已经超出了它们的作用域。具体来说，当函数被定义时，它就创建了一个闭包，这个闭包包含了该函数所使用的变量，以及一个指向函数本身的引用。

闭包在 JavaScript 中的应用非常广泛。它可以用于实现函数式编程的一些概念，如高阶函数、柯里化和延迟执行等。同时，闭包还可以用于保存函数的状态，以便在后续的调用中继续使用。

以下是一个闭包的简单示例：

```js
function makeCounter() {
  var count = 0;

  return function () {
    return count++;
  };
}

var counter = makeCounter();

console.log(counter()); // 输出 0
console.log(counter()); // 输出 1
console.log(counter()); // 输出 2
```

在上面的示例中，makeCounter 函数返回了一个函数，这个函数可以访问在 makeCounter 中定义的 count 变量，即使 makeCounter 函数已经执行完毕。由于 counter 函数是在 makeCounter 函数内部定义的，所以它可以访问 count 变量，并保持对 count 变量的引用，从而形成一个闭包。每次调用 counter 函数都会更新 count 变量的值，并返回当前的计数值。

### 闭包的优缺点

- 优点：
  - 可以访问父级作用域的变量和函数，从而实现数据的封装和隐藏，提高程序的安全性和可维护性。
  - 可以实现函数式编程的一些概念，如柯里化、延迟执行和高阶函数等，从而简化代码，提高代码的可读性和可维护性。
  - 可以保存函数的状态，以便在后续的调用中继续使用。这对于一些需要记忆状态的算法，如递归算法、迭代算法等非常有用。
- 缺点：
  - 闭包可能导致内存泄漏问题。由于闭包会维持对父级作用域的引用，所以如果不恰当地使用闭包，会导致一些变量无法被及时释放，从而占用内存空间，最终导致内存泄漏。
  - 闭包的使用可能会导致程序的性能问题。由于闭包需要维护对父级作用域的引用，所以如果闭包嵌套层数过多或者使用不当，会导致程序的性能下降。
  - 闭包的代码可能会变得比较难以理解和调试。由于闭包会涉及到多层嵌套的函数和变量，所以代码的复杂度会增加，从而降低代码的可读性和可维护性。

综上所述，闭包在 JavaScript 中具有一定的优点和缺点。在使用闭包时，需要注意避免内存泄漏和性能问题，并尽可能地保持代码的简洁和易于理解。

### mock 函数是闭包吗？

```js
function mock() {
  let a = 1;
  return {
    getA() {
      return a;
    },
  };
}
```

是的，函数mock是一个闭包。

闭包是指一个函数和其相关的引用环境的组合，其中引用环境包含函数被创建时存在的所有局部变量。在这个例子中，函数mock内部定义了一个局部变量a，并返回了一个对象，该对象包含了一个getA方法，用于获取变量a的值。

当调用mock函数时，它会返回一个对象，并将对象中的getA方法暴露出来。此时，getA方法形成了闭包，因为它可以访问其父级函数mock中定义的变量a，即使函数mock已经执行完毕并返回了。

闭包在JavaScript中有很多应用，比如实现私有变量和方法、创建模块、解决循环中的异步问题等。但是需要注意，过多的使用闭包可能会导致内存泄漏和性能问题，因为闭包中的变量和引用环境会一直存在于内存中，直到闭包被销毁或引用环境被清理。
## 参考来源

- [MDN 闭包](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures)
-
